﻿using Hx.ADSyncPlatform.ActiveDirectory;
using Hx.ADSyncPlatform.ActiveDirectory.Model;
using Hx.ADSyncPlatform.Auth;
using Hx.ADSyncPlatform.Auth.Model;
using Hx.ADSyncPlatform.Common.Config;
using Hx.ADSyncPlatform.Controllers.Base;
using Hx.ADSyncPlatform.Entity.Entities;
using Hx.ADSyncPlatform.Infrastructure.Cache;
using Hx.ADSyncPlatform.InfrastructureDateTimeExt;
using Hx.ADSyncPlatform.InfrastructureSecurity;
using Hx.ADSyncPlatform.Service;
using Hx.ADSyncPlatform.SSO.Common;
using Hx.ADSyncPlatform.SSO.Config;
using Hx.ADSyncPlatform.SSO.Model;
using Hx.ADSyncPlatform.SSO.Request;
using Hx.ADSyncPlatform.SSO.Response;
using Hx.ADSyncPlatform.WebApi.Model;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace Hx.ADSyncPlatform.Controllers
{
    /// <summary>
    /// 认证控制器
    /// </summary>
    [Route("api/auth")]
    [Authorize]
    [ApiController]
    public class AuthController : BaseController
    {
        private ActiveDirectoryClient activeDirectoryClient;
        private Plat_SysManagerService plat_SysManagerService;
        private Plat_SysRoleService plat_SysRoleService;
        private Plat_DataPermissionService plat_DataPermissionService;
        private ICacheService cacheService;
        private PermissionRequirement requirement;
        private AuthConfig authConfig;
        private AuthSource authSource;
        private DefaultRole defaultRole;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="plat_SysManagerService"></param>
        /// <param name="activeDirectoryClient"></param>
        /// <param name="plat_DataPermissionService"></param>
        /// <param name="authConfigAccessor"></param>
        /// <param name="authSourceAccessor"></param>
        /// <param name="defaultRoleAccessor"></param>
        /// <param name="plat_SysRoleService"></param>
        /// <param name="cacheService"></param>
        /// <param name="requirement"></param>
        /// <param name="serviceProvider"></param>
        public AuthController(Plat_SysManagerService plat_SysManagerService,
            ActiveDirectoryClient activeDirectoryClient,
            Plat_DataPermissionService plat_DataPermissionService
            , IOptions<AuthConfig> authConfigAccessor
            , IOptions<AuthSource> authSourceAccessor
            , IOptions<DefaultRole> defaultRoleAccessor
            , Plat_SysRoleService plat_SysRoleService, ICacheService cacheService,
            PermissionRequirement requirement, IServiceProvider serviceProvider) : base(serviceProvider)
        {
            this.plat_SysManagerService = plat_SysManagerService;
            this.plat_SysRoleService = plat_SysRoleService;
            this.plat_DataPermissionService = plat_DataPermissionService;
            this.cacheService = cacheService;
            this.requirement = requirement;
            this.authConfig = authConfigAccessor.Value;
            this.authSource = authSourceAccessor.Value;
            this.activeDirectoryClient = activeDirectoryClient;
            this.defaultRole = defaultRoleAccessor.Value;

        }


        /// <summary>
        /// 根据access_token读取用户信息  无需登陆
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>

        [HttpGet("accountInfo")]
        public async Task<Result<Person>> GetAccountInfo()
        {
            try
            {
                Guid userId = platManager.ManagerId;
                string name = platManager.Name;

                if (string.IsNullOrEmpty(name)) return await base.Fail<Person>("读取用户信息出错");
                Person person = this.activeDirectoryClient.Persons.Single(name);

                if (person == null) return await base.Fail<Person>("读取用户信息出错");

                return await Success<Person>(person);
            }
            catch (Exception ex)
            {
                return await base.Fail<Person>("读取用户信息出错");

            }

        }

        /// <summary>
        /// 取登陆用户角色，如果用户不是平台管理员，则返回默认角色Ad用户（配置文件）
        /// </summary>
        /// <param name="plat_SysManager"></param>
        /// <returns></returns>
        private string[] GetRoles(Plat_SysManager plat_SysManager = null)
        {
            List<string> roleNames = new List<string>();
            if (plat_SysManager != null)
                roleNames = plat_SysRoleService.GetRolesByUserId(plat_SysManager.UserId).Select(n => n.RoleName).ToList();
            if (!roleNames.Any()) roleNames.ToList().Add(defaultRole.Name);
            return roleNames.ToArray();
        }


        #region 私有方法

        /// <summary>
        /// 根据用户信息生成平台Token
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="userAccount"></param>
        /// <param name="roles"></param>
        /// <param name="requirement"></param>
        /// <returns></returns>
        private TokenInfoViewModel PlatToken(string userId, string userAccount, string[] roles, PermissionRequirement requirement, string loginType)
        {
            var claims = new List<Claim> {
                      new Claim(JwtRegisteredClaimNames.Jti,userId), new Claim(ClaimTypes.Name,userAccount)
                      ,new Claim("loginType",loginType)
                   };
            claims.AddRange(roles.Select(n => new Claim(ClaimTypes.Role, n)));
            var access_token = JwtService.BuildJwtToken(claims.ToArray(), requirement);
            var refresh_Token = JwtService.BuildJwtToken(claims.ToArray(), requirement, JwtService.TokenType.RefreshToken);
            return new TokenInfoViewModel() { access_token = access_token, refresh_token = refresh_Token };
        }

        #endregion


        #region 单点登陆

        /// <summary>
        /// 生成AuthorizeUrl
        /// </summary>
        /// <param name="targetUrl"></param>
        /// <returns></returns>
        [HttpGet("getAuthorizeUrl")]
        [AllowAnonymous]
        public async Task<Result<string>> GetAuthorizeUrl(string targetUrl = "")
        {
            string url = this.GetAuthRequest().GenerateAuthorizeUrl(targetUrl);
            return await base.Success<string>(url);
        }


        /// <summary>
        /// SSO登陆
        /// </summary>
        /// <param name="code">回调地址产生的code</param>
        /// <returns></returns>
        [HttpPost("ssologin")]
        [AllowAnonymous]
        public async Task<Result<TokenInfoViewModel>> SSOLogin(string code)
        {
            AuthResponse<AuthUser> authResponse = this.GetAuthRequest().Login(code);
            if (authResponse.code == ResponseStatus.Fail)
            {
                return await base.Fail<TokenInfoViewModel>(authResponse.msg);
            }
            AuthUser authUser = authResponse.data;//sso返回的用户信息
            //Person adPerson = activeDirectoryClient.Persons.Single("lvzhen1");
            Person adPerson = activeDirectoryClient.Persons.SearchInRoot(authUser.ID);
            if (adPerson == null) return await base.Fail<TokenInfoViewModel>("用户不存在");
            var manager = await plat_SysManagerService.Single(n => n.ObjectGuid == adPerson.CObjectGUID);
            string[] roles = this.GetRoles(manager);
            //根据用户生成jwt
            TokenInfoViewModel tokenResult = this.PlatToken(adPerson.CObjectGUID.ToString(), adPerson.SamAccountName, roles, requirement, "sso");
            await this.InitDataPermissions(adPerson.CObjectGUID);
            logHelper.WriteToDb(BehaviorType.Login, $"{adPerson.SamAccountName}成功登陆系统");
            return await base.Success<TokenInfoViewModel>(tokenResult);
        }

        /// <summary>
        /// sso刷新token
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpGet("refreshToken")]
        public async Task<Result<TokenInfoViewModel>> RefreshToken(string token = "")
        {
            if (token == "")
            {
                return await base.Fail<TokenInfoViewModel>("参数token为空");
            }
            if (!JwtService.TokenValid(token))
            {
                return await base.Fail<TokenInfoViewModel>("refresh_token无效");
            }
            Guid userId = JwtService.GetUserIdByToken(token);
            string loginType = JwtService.GetClaimValueByType(token, "loginType");
            string name = JwtService.GetClaimValueByType(token, ClaimTypes.Name);

            var expString = JwtService.GetClaimValueByType(token, JwtRegisteredClaimNames.Exp);
            double exp = Convert.ToDouble(expString);
            double now = DateTime.Now.GetTotalSeconds();
            if (now >= exp)
            {
                return await Token_Expired<TokenInfoViewModel>(null);
            }
            Person person = this.activeDirectoryClient.Persons.Single(name);

            if (person == null) return await base.Fail<TokenInfoViewModel>("刷新失败，用户已不存在");

            Plat_SysManager manager = null;
            if (loginType == "sso")
            {
                manager = await plat_SysManagerService.Single(n => n.ObjectGuid == person.CObjectGUID);
            }
            else
            {
                manager = await plat_SysManagerService.Single(n => n.UserId == userId);
            }
            string[] roleNames = this.GetRoles(manager);

            var tokenResult = this.PlatToken(userId.ToString(), person.SamAccountName, roleNames.ToArray(), requirement, loginType);

            await this.InitDataPermissions(userId);

            return await Success<TokenInfoViewModel>(tokenResult);


        }


        [AllowAnonymous]
        /// <summary>
        /// 单点注销
        /// </summary>
        /// <returns></returns>
        [HttpPost("ssologout")]
        public async Task<Result<string>> SSOLogout(string redirectUrl)
        {
            //redirectUrl
            var url = this.GetAuthRequest().Logout();
            return await base.Success<string>(url);
        }
        private async Task InitDataPermissions(Guid objectGuid)
        {
            string cacheKey = string.Format(dataPermissionCacheKey, objectGuid.ToString());
            List<Plat_DataPermission> dataPermissions = await this.plat_DataPermissionService.GetDataPersmissionsByObjectGuid(objectGuid);
            this.cacheService.Remove(cacheKey);
            //this.cacheService.Add(cacheKey, dataPermissions, (int)requirement.RefreshTokenExpire.TotalMinutes);
            this.cacheService.Add(cacheKey, dataPermissions);//永不过期
        }

        private AuthRequest GetAuthRequest()
        {
            AuthRequest authRequest = new AuthRequest(authConfig, authSource);
            return authRequest;
        }
        #endregion

        #region 原有登陆方式

        /// <summary>
        /// jwt登陆
        /// </summary>
        /// <param name="loginRequest"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [Obsolete]
        [HttpPost("login")]
        public async Task<Result<TokenInfoViewModel>> Login([FromBody] LoginRequest loginRequest)
        {
            var manager = await plat_SysManagerService.Single(n => n.UserName == loginRequest.UserName && n.Password ==
                      Encrypt.AESEncrypt(loginRequest.Password));

            if (manager == null) return await base.Fail<TokenInfoViewModel>("用户名或密码不正确");

            if (!manager.IsEnable) return await base.Fail<TokenInfoViewModel>("用户已被禁用");

            string[] roleNames = this.GetRoles(manager);

            var tokenResult = this.PlatToken(manager.UserId.ToString(), manager.UserName, roleNames.ToArray(), requirement, "form");

            await this.InitDataPermissions(manager.ObjectGuid);

            logHelper.WriteToDb(BehaviorType.Login, $"{loginRequest.UserName}成功登陆系统");

            return await base.Success<TokenInfoViewModel>(tokenResult);


        }
        //[HttpGet("refreshToken")]
        //public async Task<Result<TokenInfoViewModel>> RefreshToken(string token = "")
        //{

        //    if (token == "")
        //    {
        //        return await base.Fail<TokenInfoViewModel>("参数token为空");
        //    }
        //    if (!JwtService.TokenValid(token))
        //    {
        //        return await base.Fail<TokenInfoViewModel>("refresh_token无效");
        //    }
        //    Guid userId = JwtService.GetUserIdByToken(token);

        //    var expString = JwtService.GetClaimValueByType(token, JwtRegisteredClaimNames.Exp);
        //    double exp = Convert.ToDouble(expString);
        //    double now = DateTime.Now.GetTotalSeconds();
        //    if (now >= exp)
        //    {
        //        return await Token_Expired<TokenInfoViewModel>(null);
        //    }
        //    var manager = await plat_SysManagerService.Single(n => n.UserId == userId);

        //    if (manager == null) return await base.Fail<TokenInfoViewModel>("刷新失败，用户已不存在");

        //    if (!manager.IsEnable) return await base.Fail<TokenInfoViewModel>("刷新失败，用户已被禁用");

        //    string[] roleNames = this.GetRoles(manager);

        //    var tokenResult = this.PlatToken(manager.UserId.ToString(), manager.UserName, roleNames.ToArray(), requirement);

        //    await this.InitDataPermissions(manager.ObjectGuid);

        //    return await Success<TokenInfoViewModel>(tokenResult);


        //}

        //[Authorize]
        //[HttpGet("accountInfo")]
        //public async Task<Result<Plat_SysManagerViewModel>> GetAccountInfo()
        //{
        //    Guid userId = platManager.ManagerId;

        //    Person person = this.activeDirectoryClient.Persons.Single(platManager.Name);
        //    var manager = await plat_SysManagerService.Single(n => n.UserId.ToString() == userId.ToString());

        //    if (manager == null) return await base.Fail<Plat_SysManagerViewModel>("刷新失败，用户已不存在");

        //    if (!manager.IsEnable) return await base.Fail<Plat_SysManagerViewModel>("刷新失败，用户已被禁用");

        //    var res = mapper.Map<Plat_SysManagerViewModel>(manager);
        //    return await Success<Plat_SysManagerViewModel>(res);


        //}
        #endregion

    }
}
